package gdt.service;

import java.util.Calendar;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.TimeZone;

import org.springframework.binding.message.MessageBuilder;
import org.springframework.binding.message.MessageContext;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import gdt.dao.TaskDAO;
import gdt.entity.Category;
import gdt.entity.Group;
import gdt.entity.Member;
import gdt.entity.Project;
import gdt.entity.Task;
import gdt.service.utils.Validator;



public class TaskService {

	@Transactional(readOnly=true)
	public Task findById(long id){
		return taskDao.findById(id);
	}
	
	public List<Task> findByCategoryAndUser(Category category, String username){
		Member user = memberService.findByUsername(username);
		if(memberService.isAdmin(user))
			return category.getTasks();
		else
			return taskDao.findByCategoryAndUser(category, user);
	}
	
	@Transactional(propagation=Propagation.REQUIRED,readOnly=false)
	public boolean save(Task task, MessageContext messageContext){
		if(validate(task,messageContext)){
			save(task);
			return true;
		}
		else{
			return false;
		}
	}
	
	@Transactional(propagation=Propagation.REQUIRED,readOnly=false)
	public void save(Task task){
		task = taskDao.makePersistent(task);
	}
	
	@Transactional(propagation=Propagation.REQUIRED,readOnly=false)
	public void delete(Task task){
		task.getCategory().removeTask(task);
		taskDao.makeTransient(task);
	}
	

	public Task setup(Category category, String username){
		Task t = new Task();
		Member user = (Member) memberService.findByUsername(username);
		t.setAssignedMember(user);
		t.setCategory(category);
		return t;
	}
	
	
	
	public int getMinutesWorkedByGroupAndDates(Group group, Date startDate, Date endDate, TimeZone timezone){
		List<Task> tasks = new LinkedList<Task>();
		for(Project p : group.getProjects()){
			tasks.addAll(p.getAllTasks());
		}
		
		return getMinutesWorked(tasks, startDate, endDate, timezone);
	}
	
	public int getMinutesWorkedByProjectAndDates(Project project, Date startDate, Date endDate, TimeZone timezone){
		List<Task> tasks = project.getAllTasks();
		
		return getMinutesWorked(tasks, startDate, endDate, timezone);
	}
	
	public int getMinutesWorkedByProjectAndMemberAndDates(Project project, Member member, Date startDate, Date endDate, TimeZone timezone){
		List<Task> tasks = new LinkedList<Task>();
		
		for(Task task : project.getAllTasks()){
			if(task.getAssignedMember().equals(member))
				tasks.add(task);
		}
		
		return getMinutesWorked(tasks, startDate, endDate, timezone);
	}
		
		
	
	private int getMinutesWorked(List<Task> tasks, Date startDate, Date endDate, TimeZone timezone){
		long milliseconds = 0;
		
		Calendar calendar = Calendar.getInstance();
		int offset = TimeZone.getDefault().getRawOffset() - timezone.getRawOffset();
		
		calendar.setTime(startDate);
		calendar.add(Calendar.MILLISECOND, offset);
		startDate = calendar.getTime();
		
		calendar.setTime(endDate);
		calendar.add(Calendar.MILLISECOND, offset);
		endDate = calendar.getTime();
		
		
		for(Task task : tasks){
			if(task.getStartDate().before(startDate)){
				if(task.getEndDate().after(endDate)){ //Complete period
					milliseconds += endDate.getTime()  - startDate.getTime();
				}
				else if(task.getEndDate().after(startDate)){ // TS - S - TE - E
					milliseconds += task.getEndDate().getTime() - startDate.getTime();
				}
			}
			else if(task.getStartDate().before(endDate)){
				if(task.getEndDate().after(endDate)){ // S - TS - E - TE
					milliseconds += endDate.getTime() - task.getStartDate().getTime();
				}
				else{ // S - TS - TE - E
					milliseconds += task.getEndDate().getTime() - task.getStartDate().getTime();
				}
			}
		}
		
		return (int)(milliseconds/((long)(60*1000)));
	}
	
	
	
	/********************************* DAO's *********************************/
	
	private MemberService memberService;

	public void setMemberService(MemberService memberService) {
		this.memberService = memberService;
	}
	
	private TaskDAO taskDao;
	
	public void setTaskDao(TaskDAO taskDao) {
		this.taskDao = taskDao;
	}


	/******************************* Validation *******************************/
	
	private Validator validator;
	
	public boolean validate(Task task, MessageContext context){
		boolean valid = validator.validate(task, context);
		
		if(task.getStartDate().after(task.getEndDate())){
			context.addMessage((new MessageBuilder()).error().code("error_validating_dates").build());
			valid = false;
		}
		
		return valid;
	}
	
	public Validator getValidator() {
		return validator;
	}

	public void setValidator(Validator validator) {
		this.validator = validator;
	}
	
}
